#ifndef XG_RESPOOL_H
#define XG_RESPOOL_H
//////////////////////////////////////////////////////////////////////////////
#include "File.h"

template<typename T>
class ResPool : public Object
{
	class Data
	{
	public:
		int num;
		bool free;
		sp<T> data;
		time_t utime;
		
		sp<T> get()
		{
			utime = time(NULL);
			free = false;
			num++;

			return data;
		}
		Data(sp<T> data)
		{
			update(data);
		}
		void update(sp<T> data)
		{
			this->num = 1;
			this->free = false;
			this->utime = time(NULL);
			std::swap(this->data, data);
		}
	};

protected:
	int maxlen;
	int timeout;
	string clazz;
	SpinMutex mtx;
	vector<Data> vec;
	function<sp<T>()> creator;

	sp<T> grasp()
	{
		auto get = [&](){
			int len;
			sp<T> res;
			Data* dest = NULL;
			time_t now = time(NULL);

			{
				int free = 0;
				SpinLocker lk(mtx);

				len = vec.size();

				for (Data& item : vec)
				{
					if (item.free)
					{
						if (item.num < 100 && item.utime + timeout > now) return item.get();

						dest = &item;
						free++;
					}
				}

				if (dest)
				{
					if (len == free)
					{
						vec.clear();
						dest = NULL;
						len = 0;
					}
					else
					{
						dest->free = false;
					}
				}
			}

			if (dest)
			{
				if (res = creator())
				{
					SpinLocker lk(mtx);

					dest->update(res);
				}
				else
				{
					SpinLocker lk(mtx);

					dest->free = true;
				}
			}
			else
			{
				if (len >= maxlen) return res;

				if (res = creator())
				{
					SpinLocker lk(mtx);

					if (vec.size() < maxlen) vec.push_back(res);
					if (clazz.empty()) clazz = res->getClassName();
				}
			}

			return res;
		};

		sp<T> data = get();

		if (data) return data;

		Timer tr(NULL);

		while (true)
		{
			Sleep(10);

			if (data = get()) return data;

			if (tr.getTimeGap() > 1000000) break;
		}

		int len = TaskQueue::Instance()->getThreads() >> 1;

		len = min(max(maxlen, len), 20);

		mtx.lock();

		if (maxlen < len)
		{
			maxlen = len;

			mtx.unlock();

			if (data = get()) return data;

			mtx.lock();
		}

		if (clazz.empty())
		{
			LogTrace(eERR, "grasp resource[%s] failed", getClassName());
		}
		else
		{
			LogTrace(eERR, "grasp resource[%s][%s] timeout", getClassName(), clazz.c_str());
		}

		mtx.unlock();

		return data;
	}

public:
	sp<T> get()
	{
		if (timeout <= 0) return creator();

		return sp<T>(grasp().get(), [&](T* ptr){
			SpinLocker lk(mtx);

			for (Data& item : vec)
			{
				if (ptr == item.data.get())
				{
					item.free = true;

					break;
				}
			}
		});
	}
	int getLength() const
	{
		return maxlen;
	}
	int getTimeout() const
	{
		return timeout;
	}
	void disable(sp<T>& data)
	{
		{
			SpinLocker lk(mtx);

			for (Data& item : vec)
			{
				if (data == item.data)
				{
					item.utime = 0;

					break;
				}
			}
		}

		data = NULL;
	}
	void setCreator(function<sp<T>()> creator)
	{
		this->creator = creator;
	}
	ResPool(int maxlen = 8, int timeout = 30)
	{
		this->timeout = timeout;
		this->maxlen = maxlen;
	}
	ResPool(function<sp<T>()> creator, int maxlen = 8, int timeout = 30)
	{
		this->creator = creator;
		this->timeout = timeout;
		this->maxlen = maxlen;
	}
};
//////////////////////////////////////////////////////////////////////////////
#endif